package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.OptionalRangeEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.help.PaasFormHelp;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.pro.detailtab.dto.MarketActivityEffectGetDTO;
import com.xbongbong.pro.detailtab.vo.MarketActivityEffectGetVO;
import com.xbongbong.pro.detailtab.vo.MarketActivityEffectTargetVO;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.statistic.result.pojo.vo.MarketActivityEffectDataVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.SaleStageEntity;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.MarketActivityEffectTargetEnum;
import com.xbongbong.saas.enums.business.MarketActivityThroughEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.dictionary.ClueStatusEnum;
import com.xbongbong.saas.model.SaleStageModel;
import com.xbongbong.saas.service.MarketActivityEffectService;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.enums.DataPermissionEnum;
import com.xbongbong.sys.model.UserModel;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ScriptedMetricAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @Auther: yingjianfeng
 * @Date: 2022/3/15 11:22
 * @Description:
 */
@Slf4j
@Service("marketActivityEffectService")
public class MarketActivityEffectServiceImpl implements MarketActivityEffectService {

    @Resource
    private EsHelper esHelper;
    @Resource
    private UserModel userModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaasFormHelp paasFormHelp;
    @Resource
    private SaleStageModel saleStageModel;

    @Override
    public MarketActivityEffectGetVO getMarketActivityEffect(MarketActivityEffectGetDTO marketActivityEffectGetDTO) throws XbbException {
        MarketActivityEffectGetVO marketActivityEffectGetVO = new MarketActivityEffectGetVO();
        MarketActivityEffectTargetVO marketActivityEffectTargetVO = new MarketActivityEffectTargetVO();
        try {
            marketActivityEffectTargetVO = getMarketActivityEffectTargetVO(marketActivityEffectGetDTO);
            //封装成前端需要的数据格式
            packageActivityEffect(marketActivityEffectTargetVO, marketActivityEffectGetVO);
        } catch (XbbException e) {
            log.error(" com.xbongbong.saas.service.impl.SaasDetailTabServiceImpl.getMarketActivityEffect  查询活动成效发生异常: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return marketActivityEffectGetVO;
    }

    private void packageActivityEffect(MarketActivityEffectTargetVO marketActivityEffectTargetVO, MarketActivityEffectGetVO marketActivityEffectGetVO) {
        //组装信息 返回给前端
        List<List<MarketActivityEffectDataVO>> activityEffectList = new ArrayList<List<MarketActivityEffectDataVO>>();
        //第一行指标（线索数量 有效线索数量 转换线索数量 客户数量）
        List<MarketActivityEffectDataVO> firstTargetList = new ArrayList<>();
        MarketActivityEffectDataVO clueTotalObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.ALL_CLUE_COUNT.getName(), MarketActivityEffectTargetEnum.ALL_CLUE_COUNT.getUnit(), "saleClue", String.valueOf(marketActivityEffectTargetVO.getClueCount()), MarketActivityThroughEnum.ALL_CLUE.getCode());
        MarketActivityEffectDataVO effectiveCLueCountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.EFFECTIVE_CLUE_COUNT.getName(), MarketActivityEffectTargetEnum.EFFECTIVE_CLUE_COUNT.getUnit(), "saleClue", String.valueOf(marketActivityEffectTargetVO.getEffectiveClueCount()), MarketActivityThroughEnum.EFFECTIVE_CLUE.getCode());
        MarketActivityEffectDataVO transformedCountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.TRANSFORMED_CLUE_COUNT.getName(), MarketActivityEffectTargetEnum.TRANSFORMED_CLUE_COUNT.getUnit(), "saleClue", String.valueOf(marketActivityEffectTargetVO.getTransformedClueCount()), MarketActivityThroughEnum.TRANSFORMED_CLUE.getCode());
        MarketActivityEffectDataVO customerCountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.CUSTOMER_COUNT.getName(), MarketActivityEffectTargetEnum.CUSTOMER_COUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getCustomerCount()), null);
        firstTargetList.add(clueTotalObj);
        firstTargetList.add(effectiveCLueCountObj);
        firstTargetList.add(transformedCountObj);
        firstTargetList.add(customerCountObj);
        //第二行指标（线索转客户转化率 机会数量 机会金额 线索转机会转化率）
        List<MarketActivityEffectDataVO> secondTargetList = new ArrayList<>();
        DecimalFormat df = new DecimalFormat("0.00");
        //线索转客户转化率 = 客户数量/线索数量  (因为是按照百分比展示，所以先*100)
        MarketActivityEffectDataVO clueCustomerRateObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.CLUE_CUSTOMER_RATE.getName(), MarketActivityEffectTargetEnum.CLUE_CUSTOMER_RATE.getUnit(), "", marketActivityEffectTargetVO.getClueCustomerRate(), null);
        MarketActivityEffectDataVO allOpportunityCountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.ALL_OPPORTUNITY_COUNT.getName(), MarketActivityEffectTargetEnum.ALL_OPPORTUNITY_COUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getAllOpportunityCount()), null);
        MarketActivityEffectDataVO allOpportunityAmountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.ALL_OPPORTUNITY_AMOUNT.getName(), MarketActivityEffectTargetEnum.ALL_OPPORTUNITY_AMOUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getAllOpportunityAmount()), null);
        //线索转机会转化率 = 关联机会的客户/线索数量
        MarketActivityEffectDataVO clueOpportunityRateObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.CLUE_OPPORTUNITY_RATE.getName(), MarketActivityEffectTargetEnum.CLUE_OPPORTUNITY_RATE.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getClueOpportunityRate()), null);
        secondTargetList.add(clueCustomerRateObj);
        secondTargetList.add(allOpportunityCountObj);
        secondTargetList.add(allOpportunityAmountObj);
        secondTargetList.add(clueOpportunityRateObj);
        //第三行指标 （赢单机会数量 赢单机会金额 合同订单数 合同订单金额）
        List<MarketActivityEffectDataVO> thirdTargetList = new ArrayList<>();
        MarketActivityEffectDataVO winOpportunityCountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.WIN_OPPORTUNITY_COUNT.getName(), MarketActivityEffectTargetEnum.WIN_OPPORTUNITY_COUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getWinOpportunityCount()), null);
        MarketActivityEffectDataVO winOpportunityAmountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.WIN_OPPORTUNITY_AMOUNT.getName(), MarketActivityEffectTargetEnum.WIN_OPPORTUNITY_AMOUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getWinOpportunityAmount()), null);
        // 合同订单数
        MarketActivityEffectDataVO allContractCountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.ALL_CONTRACT_COUNT.getName(), MarketActivityEffectTargetEnum.ALL_CONTRACT_COUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getAllContractCount()), null);
        // 合同订单金额
        MarketActivityEffectDataVO allContractAmountObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.ALL_CONTRACT_AMOUNT.getName(), MarketActivityEffectTargetEnum.ALL_CONTRACT_AMOUNT.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getAllContractAmount()), null);
        thirdTargetList.add(winOpportunityCountObj);
        thirdTargetList.add(winOpportunityAmountObj);
        thirdTargetList.add(allContractCountObj);
        thirdTargetList.add(allContractAmountObj);
        //第四行指标 （线索转合同转化率）
        List<MarketActivityEffectDataVO> fourthTargetList = new ArrayList<>();
        MarketActivityEffectDataVO clueContractRateObj = new MarketActivityEffectDataVO(MarketActivityEffectTargetEnum.CLUE_CONTRACT_RATE.getName(), MarketActivityEffectTargetEnum.CLUE_CONTRACT_RATE.getUnit(), "", String.valueOf(marketActivityEffectTargetVO.getClueContractRate()), null);
        fourthTargetList.add(clueContractRateObj);

        activityEffectList.add(firstTargetList);
        activityEffectList.add(secondTargetList);
        activityEffectList.add(thirdTargetList);
        activityEffectList.add(fourthTargetList);
        marketActivityEffectGetVO.setActivityEffectList(activityEffectList);
    }

    /**
     * 返回活动成效相关指标
     *
     * @param marketActivityEffectGetDTO
     * @return
     * @throws XbbException
     */
    @Override
    public MarketActivityEffectTargetVO getMarketActivityEffectTargetVO(MarketActivityEffectGetDTO marketActivityEffectGetDTO) throws XbbException {
        MarketActivityEffectTargetVO marketActivityEffectTargetVO = new MarketActivityEffectTargetVO();
        //“线索数量”，“有效线索数量”,"转化线索数量","客户数量" , "线索转客户转化率" （五个指标）
        setClueAndCustomerTarget(marketActivityEffectGetDTO, marketActivityEffectTargetVO);
        //已转化的客户id
        List<Long> transformedCustomerIds = marketActivityEffectGetDTO.getTransformedCustomerIds();
        //已转换线索的id
        List<Long> transformedClueIds = marketActivityEffectGetDTO.getTransformedClueIds();
        // 如果线索都没有转客户了 那机会和合同相关指标都不需要计算了
        if (CollectionsUtil.isNotEmpty(transformedCustomerIds) && CollectionsUtil.isNotEmpty(transformedClueIds)) {
            CountDownLatch latch = new CountDownLatch(3);
            try {
                //线索转机会转化率
                setClueOpportunityRateTarget(marketActivityEffectGetDTO.getCorpid(), transformedClueIds, marketActivityEffectTargetVO, latch);
                //"机会数量""机会金额""赢单机会数""赢单机会金额" （四个指标）
                setLinkOpportunityTarget(marketActivityEffectGetDTO, transformedCustomerIds, marketActivityEffectTargetVO, latch);
                // 合同订单数 合同订单金额 线索转合同转化率
                setLinkContractTarget(marketActivityEffectGetDTO, transformedCustomerIds, marketActivityEffectTargetVO, latch);
            } catch (XbbException e) {
                log.error("活动成效查询机会和合同异常", e);
            } finally {
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    log.error("latch.await()异常", e);
                }
            }
        }
        return marketActivityEffectTargetVO;

    }

    /**
     * 设置线索和客户相关的指标
     *
     * @return
     */
    private void setClueAndCustomerTarget(MarketActivityEffectGetDTO marketActivityEffectGetDTO, MarketActivityEffectTargetVO marketActivityEffectTargetVO) throws XbbException {
        List<Long> customerIds = null;
        List<Long> transformedClueIds = null;
        List<Long> clueIds = new ArrayList<>();
        Long marketActivityId = marketActivityEffectGetDTO.getDataId();
        String corpId = marketActivityEffectGetDTO.getCorpid();
        UserVO loginUser = marketActivityEffectGetDTO.getLoginUser();
        Integer clueCount = 0;
        Integer effectiveClueCount = 0;
        Integer transformedClueCount = 0;
        Integer customerCount = 0;
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpId));
        boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.MARKET_ACTIVITY_ID), marketActivityId));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        // fix 需要排除掉审批中到数据
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        //添加角色数据权限过滤
        setDataPermission(loginUser, boolQueryBuilder, IndexTypeEnum.IDX_SAAS_CLUE_TEAM);
        //添加"表单权限"过滤
        setFormPermission(loginUser, boolQueryBuilder);
        sourceBuilder.query(boolQueryBuilder);
        //按照线索状态分组聚合
        ValuesSourceAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STATUS)).size(Integer.MAX_VALUE);
        //自定义脚本聚合,分组后组内聚合 线索id ，返回到一个数组中，用于查询客户.
        /*
         * scripted_metric" : {
         *  "init_script": "state.clueIds=[]",
         *             "map_script": ""doc.dataId.value == null ? null : state.clueIds.add(doc.dataId.value)"",
         *             "combine_script": "state.clueIds",
         *             "reduce_script": "states.stream().flatMap(Collection::stream).toArray()" //注意这里是 states 比上面多了一个 s
         *           }
         *
         */
        ScriptedMetricAggregationBuilder scriptedMetricAggregationBuilder = AggregationBuilders
                .scriptedMetric("agg")
                .initScript(new Script("state.clueIds = []"))
                .mapScript(new Script("doc.dataId.value == null ? null : state.clueIds.add(doc['" + StringConstant.DATA_ID + "'].value)"))
                .combineScript(new Script("state.clueIds"))
                //注意这里是 states 比上面多了一个 s
                .reduceScript(new Script("states.stream().flatMap(Collection::stream).toArray()"));
        termsAggregationBuilder.subAggregation(scriptedMetricAggregationBuilder);
        JSONObject resultJsonObject;
        try {
            resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, termsAggregationBuilder);
        } catch (Exception e) {
            log.error("SaasGetHelp  getClueCountAndCustomerIds 查询es 发生异常,错误 boolQueryBuilder-{}-,termsAggregationBuilder-{}-", boolQueryBuilder.toString(), termsAggregationBuilder.toString());
            //直接返回
            return;
        }
        // buckets 的结果，数据结构如下，数据节点层级的是在 esHelper 中 设置好的)
           /*
        "aggregations": {
            "aggs01": {
                "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "1",  ##  key 是 线索阶段Code （ClueEnum.CLUE_STATUS text_5 ）,线索阶段 ClueStatusEnum
                                "doc_count": 2,
                                "aggs02": {
                                "value": [   ## value 是一个数组，存放的是线索关联的客户的id （ClueEnum.CUSTOMER_ID text_11）
                                    "2",
                                    "1"
                                ]
                            }
                            },
                            {
                                "key": "2",
                                "doc_count": 1,
                                "aggs02": {
                                "value": [
                                "3"
                                ]
                            }
                            },
                            {
                                "key": "3",
                                "doc_count": 1,
                                "aggs02": {
                                "value": [
                                null
                                ]
                            }
                            }
                            ]
            }
        }
         */
        if (Objects.nonNull(resultJsonObject)) {
            //数据节点结构在上面注释中
            JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
            if (Objects.nonNull(bucketsArray)) {
                for (Object object : bucketsArray) {
                    if (object != null) {
                        JSONObject json = (JSONObject) object;
                        //线索状态对应的code值
                        String statusCode = json.getString("key");
                        //该线索状态的线索数
                        Integer count = json.getInteger("doc_count");
                        //线索总数 = 不同状态的线索数据总和
                        clueCount += count;
                        //有效线索数 = 不是无效状态的线索数总和
                        if (!statusCode.equals(ClueStatusEnum.INVALID.getCode())) {
                            effectiveClueCount += count;
                        }
                        //转换线索数 = 状态为"已转换"的线索总数
                        if (statusCode.equals(ClueStatusEnum.TRANSFORMED.getCode())) {
                            transformedClueCount += count;

                        }
                        //获取线索id，用于查询客户
                        JSONObject aggs02 = json.getJSONObject("scripted_metric#agg");
                        if (Objects.nonNull(aggs02)) {
                            JSONArray clueIdsArray = aggs02.getJSONArray("value");
                            if (Objects.nonNull(clueIdsArray) && clueIdsArray.size() > 0) {
                                List<Long> ids = JSONArray.parseArray(clueIdsArray.toJSONString(), Long.class);
                                List<Long> clueIdList = ids.stream().distinct().filter(id -> Objects.nonNull(id)).collect(toList());
                                if (statusCode.equals(ClueStatusEnum.TRANSFORMED.getCode())) {
                                    //获取已转换的线索id集合
                                    transformedClueIds = clueIdList;
                                }
                                //去重，过滤空
                                clueIds.addAll(clueIdList);
                            }
                        }

                    }
                }

            }

        }
        //获取线索转化的客户数据（通过已转换的线索id去关联查询）
        if (CollectionsUtil.isNotEmpty(clueIds)) {
            BoolQueryBuilder boolQuery = boolQuery();
            boolQuery.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpId));
            boolQuery.filter(termsQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CLUE_ID), clueIds));
            boolQuery.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            List<PaasFormDataEntityExt> customerDataList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CUSTOMER, boolQuery, PaasFormDataEntityExt.class, Arrays.asList(StringConstant.DATA_ID));
            if (CollectionsUtil.isNotEmpty(customerDataList)) {
                customerIds = customerDataList.stream().map(item -> item.getDataId()).collect(Collectors.toList());
            }

        }
        //线索转客户转化率
        if (CollectionsUtil.isNotEmpty(customerIds) && clueCount > 0) {
            customerCount = customerIds.size();
            DecimalFormat df = new DecimalFormat("0.00");
            marketActivityEffectTargetVO.setClueCustomerRate(df.format(((double) customerCount / clueCount) * 100));
        }
        marketActivityEffectTargetVO.setClueCount(clueCount);
        marketActivityEffectTargetVO.setEffectiveClueCount(effectiveClueCount);
        marketActivityEffectTargetVO.setTransformedClueCount(transformedClueCount);
        marketActivityEffectTargetVO.setCustomerCount(customerCount);
        //封装查询条件,用于计算客户和机会相关的指标
        marketActivityEffectGetDTO.setTransformedClueIds(transformedClueIds);
        marketActivityEffectGetDTO.setTransformedCustomerIds(customerIds);
    }

    /**
     * 设置线索机会转化率
     */
    @Async("staticsMarketData")
    public void setClueOpportunityRateTarget(String corpId, List<Long> clueIds, MarketActivityEffectTargetVO marketActivityEffectTargetVO, CountDownLatch latch) {
        try {
            Integer linkOpportunityClueCount = 0;
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpId));
            boolQueryBuilder.filter(termsQuery("clueId", clueIds));
            boolQueryBuilder.must(existsQuery("saleOpportunityId"));
            boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            // fix 需要排除掉审批中到数据
            boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
            sourceBuilder.query(boolQueryBuilder);
            /*
            去重聚合
            请求信息
            "aggs" : {
            "distinct_colors" : {
                "cardinality" : {
                  "field" : "data.text_11"
                }
              }
            }
            响应信息
            "aggregations": {
                "aggs01": {
                    "value": 5
                }
            }
            */
            //去重聚合(clueId去重)
            CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality("aggs01").field("saleOpportunityId");
            JSONObject resultJsonObject;
            try {
                resultJsonObject = esHelper.multElasticAggs4NoBuckets2Object(IndexTypeEnum.IDX_STATICS_MARKET_DATA, boolQueryBuilder, Arrays.asList(cardinalityAggregationBuilder));
            } catch (Exception e) {
                log.error("SaasGetHelp  linkOpportunityCustomerCount 查询es 发生异常,错误 boolQueryBuilder-{}-,cardinalityAggregationBuilder-{}-", boolQueryBuilder.toString());
                //直接返回 0
                return;
            }
            JSONObject aggregations = resultJsonObject.getJSONObject("aggregations");
            if (Objects.nonNull(aggregations)) {
                JSONObject aggs01 = aggregations.getJSONObject("aggs01");
                if (Objects.nonNull(aggs01)) {
                    linkOpportunityClueCount = Objects.isNull(aggs01.getInteger("value")) ? 0 : aggs01.getInteger("value");
                    if (linkOpportunityClueCount > 0) {
                        //设置 线索转机会转化率 （ （1）公式 = 已转机会的线索数 / 线索总数 (2) 已转机会的线索数:线索转客户，客户下有机会，这条线索才算;(3)三表查询，太难了，直接用国徽的数据了）
                        DecimalFormat df = new DecimalFormat("0.00");
                        String clueOpportunityRate = df.format(((double) linkOpportunityClueCount / marketActivityEffectTargetVO.getClueCount()) * 100);
                        marketActivityEffectTargetVO.setClueOpportunityRate(clueOpportunityRate);
                    }
                }
            }
        } catch (Exception e) {
            log.error("setClueOpportunityRateTarget error", e);
        } finally {
            latch.countDown();
        }
    }

    /**
     * 返回 活动成效 ，机会相关的指标
     *
     * @param marketActivityEffectGetDTO
     * @param customerIdList
     * @return
     * @throws XbbException
     */
    @Async("staticsMarketData")
    public void setLinkOpportunityTarget(MarketActivityEffectGetDTO marketActivityEffectGetDTO, List<Long> customerIdList, MarketActivityEffectTargetVO marketActivityEffectTargetVO, CountDownLatch latch) throws XbbException {
        try {

            Double allOpportunityAmount = 0.00;
            Integer allOpportunityCount = 0;
            Double winOpportunityAmount = 0.00;
            Integer winOpportunityCount = 0;
            //按照"机会阶段"分组,对机会金额求和
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, marketActivityEffectGetDTO.getCorpid()));
            boolQueryBuilder.filter(termsQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.CUSTOMER_NAME), customerIdList));
            boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            // fix 需要排除掉审批中到数据
            boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
//        ValuesSourceAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE)).size(Integer.MAX_VALUE);
            ValuesSourceAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.STAGE_ID)).size(Integer.MAX_VALUE);
            // 对每组的 data.num_2  求和（机会金额）
            termsAggregationBuilder.subAggregation(AggregationBuilders.sum("costTotal").field(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.ESTIMATE_AMOUNT)));
            JSONObject resultJsonObject = null;
            try {
                resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_OPPORTUNITY, boolQueryBuilder, termsAggregationBuilder);
            } catch (Exception e) {
                //发生异常，返回 机会数量为 0
                log.error("getLinkOpportunity,es 查询机会指标发生异常", e.getMessage());
                return;
            }
            JSONArray oppoBucketsArray = resultJsonObject.getJSONArray("buckets");
            if (oppoBucketsArray != null) {
                List<SaleStageEntity> saleStageList = saleStageModel.getList(marketActivityEffectGetDTO.getCorpid(), null, null, StageTypeEnum.SUCCESS_STAGE.getType());
                List<Long> saleStageIdList = saleStageList.stream().map(SaleStageEntity::getId).collect(Collectors.toList());
                for (Object object : oppoBucketsArray) {
                    if (object != null) {
                        //判断空
                        JSONObject json = (JSONObject) object;
                        //key 是 机会阶段
//                    Integer stageCode = json.getInteger("key");
                        Integer count = getIntegerDefalutValue(json.getInteger("doc_count"));
                        Double value = getIntegerDefalutValue(json.getJSONObject("costTotal").getDouble("value"));
                        allOpportunityAmount += value;
                        allOpportunityCount += count;
                        //赢单阶段
                        // 【按照stageId聚合统计，阶段id是否为成功阶段】
//                    if(Integer.valueOf(SaleStageEnum.DEAL.getCode()).equals(stageCode)){
//                        winOpportunityAmount += value;
//                        winOpportunityCount += count;
//                    }
                        Long stageId = json.getLong("key");
                        if (saleStageIdList.contains(stageId)) {
                            winOpportunityAmount += value;
                            winOpportunityCount += count;
                        }
                    }
                }
            }
            marketActivityEffectTargetVO.setAllOpportunityCount(allOpportunityCount);
            marketActivityEffectTargetVO.setAllOpportunityAmount(allOpportunityAmount);
            marketActivityEffectTargetVO.setWinOpportunityAmount(winOpportunityAmount);
            marketActivityEffectTargetVO.setWinOpportunityCount(winOpportunityCount);
        } catch (Exception e) {
            log.error("setLinkOpportunityTarget error", e);
        } finally {
            latch.countDown();
        }
    }

    /**
     * 设置合同相关指标
     * date: 2022/3/10 10:26 上午
     *
     * @param marketActivityEffectGetDTO:
     * @param customerIdList:
     * @param marketActivityEffectTargetVO:
     * @author yingjf
     */
    @Async("staticsMarketData")
    public void setLinkContractTarget(MarketActivityEffectGetDTO marketActivityEffectGetDTO, List<Long> customerIdList, MarketActivityEffectTargetVO marketActivityEffectTargetVO, CountDownLatch latch) throws XbbException {
        try {

            // 合同订单数
            Integer allContractCount = 0;
            // 合同订单金额
            Double allContractAmount = 0.00;
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            //解析查询条件
            boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, marketActivityEffectGetDTO.getCorpid()));
            boolQueryBuilder.filter(termsQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER), customerIdList));
            // 排除红冲合同
            boolQueryBuilder.filter(termQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.IS_RED), 0));
            boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            // fix 需要排除掉审批中到数据
            boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
            SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum("agg001").field(ContractEnum.getEsAttr4Keyword(ContractEnum.AMOUNT));
            ValueCountAggregationBuilder countAggregationBuilder = AggregationBuilders.count("aggs02").field(StringConstant.DATA_ID);
            List<ValuesSourceAggregationBuilder> params = new ArrayList<>();
            params.add(sumAggregationBuilder);
            params.add(countAggregationBuilder);
            JSONObject jsonObject = esHelper.multElasticAggs4NoBuckets2Object(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryBuilder, params);
            JSONObject aggregations = jsonObject.getJSONObject("aggregations");
            JSONObject aggs011 = aggregations.getJSONObject("agg001");
            allContractAmount = aggs011.getDoubleValue("value");
            JSONObject aggs012 = aggregations.getJSONObject("aggs02");
            allContractCount = aggs012.getInteger("value");
            marketActivityEffectTargetVO.setAllContractCount(allContractCount);
            marketActivityEffectTargetVO.setAllContractAmount(allContractAmount);
            DecimalFormat df = new DecimalFormat("0.00");
            // fix 是否确认下合同总数是否为null，避免出现空指针异常
            String clueOpportunityRate = df.format(((double) allContractCount / marketActivityEffectTargetVO.getClueCount()) * 100);
            marketActivityEffectTargetVO.setClueContractRate(clueOpportunityRate);
        } catch (Exception e) {
            log.error("setLinkContractTarget error", e);
        } finally {
            latch.countDown();
        }
    }

    /**
     * 关联查询设置数据权限
     * 逻辑解释：
     * 对应拥有"线索线索"模块"全公司权限" DataPermissionEnum.ALL 的人员，统计的是所有线索（包含公海池,是不是很骚,所以不需要添加userId限制）
     * 其他角色的，就是按照角色权限来，在es查询时，添加userId过滤
     *
     * @param
     * @param boolQueryBuilder
     */
    private void setDataPermission(UserVO userVO, BoolQueryBuilder boolQueryBuilder, IndexTypeEnum indexTypeEnum) {
        //判断员工是否拥有“线索”模块的最大数据权限
        Boolean haveAllDataPermission = false;
        Map<String, Integer> dataPermissionDetail = userVO.getDataPermissionDetail();
        if (Objects.nonNull(dataPermissionDetail) && dataPermissionDetail.size() > 0) {
            Integer clueDataPermission = dataPermissionDetail.get(SaasMarkEnum.SAAS.getCode() + "_" + XbbRefTypeEnum.CLUE.getCode());
            haveAllDataPermission = DataPermissionEnum.ALL.getCode().equals(clueDataPermission);
        }
        //如果是最大数据权限，则不需要添加 任何的userId 限制，因为“公海池“的线索也需要被统计
        if (haveAllDataPermission) {
            return;
        }
        //不是最大权限，则需要添加userId的权限限制
        UserEntity userEntity = new UserEntity();
        BeanUtil.copyProperties(userVO, userEntity);
        Integer dataPermission = userModel.getDataPermission(userEntity);
        List<String> userIdIn = userModel.getSubIdListByDataPermission(dataPermission, userVO.getUserId(), userVO.getCorpid(), BasicConstant.ZERO, false);
        HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(indexTypeEnum, userVO.getCorpid(), null, userIdIn);
        boolQueryBuilder.must(hasChildQueryBuilder);
    }

    /**
     * 设置表单权限
     *
     * @param userVO
     * @param boolQueryBuilder
     */
    private void setFormPermission(UserVO userVO, BoolQueryBuilder boolQueryBuilder) {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", userVO.getCorpid());
        param.put("businessType", XbbRefTypeEnum.CLUE.getCode());
        param.put("enable", 1);
        param.put("del", DelEnum.NORMAL.getDel());
        List<PaasFormEntityExt> list = paasFormModel.list(param);
        Boolean noPermission;
        //获取用户有使用权限的表达 id集合
        List<Long> formIdIn = new ArrayList<>();
        if (CollectionsUtil.isNotEmpty(list)) {
            for (PaasFormEntityExt paasFormEntityExt : list) {
                List<OptionalRangeEntity> optionalRangeList = JSONObject.parseArray(paasFormEntityExt.getPermission(), OptionalRangeEntity.class);
                noPermission = CollectionsUtil.isNotEmpty(optionalRangeList) && paasFormHelp.haveNoPermission(userVO, optionalRangeList);
                //去除没有使用权限的表单,noPermission为true  表示没有使用权限
                if (!noPermission) {
                    formIdIn.add(paasFormEntityExt.getId());
                }
            }
        }
        if (CollectionsUtil.isNotEmpty(formIdIn)) {
            boolQueryBuilder.filter(termsQuery("formId", formIdIn));
        } else {
            //没有使用权限的表单 传输-1
            boolQueryBuilder.filter(termQuery("formId", -1));
        }
    }

    private Integer getIntegerDefalutValue(Integer value) {
        return value == null ? 0 : value;
    }

    private Double getIntegerDefalutValue(Double value) {
        return value == null ? 0.00 : value;
    }
}
